import Event from 'modules/Event';
import TouchEvent from './core';
import M from './M';
import { TOUCH_DIRECTION } from './config';
import util from './util';
import Tap from './Tap';

/**
 * 获取X,Y轴上的变更，对于没有scale == 1的元素，需要对滑动变更进行锁定
 * 避免左右滑动的时候触发上下滑动
 * @param {*} change
 * @param {*} state
 * @returns
 */
function getChangeXY(change, state) {
    if (state.scale == 1) {
        if (state.direction === TOUCH_DIRECTION.leftToRight) {
            return [change.x, 0];
        }
        if (state.direction === TOUCH_DIRECTION.topToBottom) {
            return [0, change.y];
        }
    }
    return [change.x, change.y];
}

export default class Pinch extends Event {
    matrix = [ // 初始状态
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1,
    ]

    // 被缩放对象是否需要加载
    isLoad = false // 是否可以立即开启

    constructor(
        $eventDom, // 事件监听元素
        $transformDom = $eventDom, // 被缩放元素
        {
            $borderCheckDom = $eventDom, // 边界校验元素
            canPinch = true, // 是否可缩放
            maxScale = 1,
            minScale = 1,
        } = {},
    ) {
        super();
        this.canPinch = canPinch;
        this.maxScale = maxScale;
        this.minScale = minScale;
        $transformDom.style.cssText += ';transform-origin: left top; will-change: transform;';

        this.$eventDom = $eventDom;
        this.$transformDom = $transformDom;
        this.$borderCheckDom = $borderCheckDom;

        // 需要处理是否是图片，是否需要加载的问题
        if ($transformDom.tagName.toLowerCase() === 'img') {
            this.updateInfo($transformDom);
            $transformDom.$$pinch = this;
        } else {
            this.isLoad = true;
        }
        this.origin = util.getOrigin(this.$transformDom);
        // 未加载，允许左右移动，不允许缩放图片
        this.baseOrigin = util.getOrigin(this.$borderCheckDom);
        this.addEvents();
    }

    // 获取图片信息
    updateInfo = ($img = this.$transformDom) => {
        const load = () => {
            $img.removeEventListener('load', load);
            if (this.isDestroy) return;
            // 图片加载成功，不代表一定可以获取到图片的rect信息，
            // 因此这里做个延迟判断，足以覆盖大多数情况，从严谨的角度来讲，可以直接根据img.naturalWidth/Height算出来
            this.timeout = setTimeout(() => {
                this.origin = util.getOrigin(this.$transformDom);
                this.baseOrigin = util.getOrigin(this.$borderCheckDom);
                this.naturalWidth = $img.naturalWidth;
                this.maxScale = this.naturalWidth / this.origin.width;
                this.isLoad = true;
            }, 100);
        };
        $img.addEventListener('load', load);
        $img.src = $img.src || $img.originSrc;
        if ($img.complete || $img.width || $img.height) {
            load();
        }
    }

    addEvents() {
        let m = []; // 矩阵变化

        this.touch = new TouchEvent(this.$eventDom, {
            finger: 2,
            preventDefault: true,
            XYWeight: 2, // x 相对于 y 的权重比例
        })
            // onStart之前执行
            .on('nativeStart', () => {
                this.trigger('nativeStart');
                // 立即终结动画
                this.animationEnd && this.animationEnd();
            })
            .on('start', () => {
                m = [...this.matrix];
                // onStart()
                this.trigger('start');
            })
            .on('change', (change, state) => {
                if (state.finger >= 2) {
                    if (this.isLoad && this.canPinch) {
                        // 需要
                        m = M.trans(state.scalePer, state.move, this.origin, M.scale(state.scalePer, m));
                    }
                } else {
                    const [changeX, changeY] = getChangeXY(change, state);
                    // 单指拖拽
                    m = M.translate(changeX, changeY, this.matrix);

                    const result = util.borderCheck(m, this.origin, this.baseOrigin);
                    if (state.finger == 1 && result.isOver) {
                        m = result.m;
                    }

                    // 没有溢出阻止默认事件
                    //  && event.preventDefault()
                    if (result.isOver) {
                        this.trigger('change', result, state); // 触发变化
                    }
                }
                util.RAF(() => {
                    // 触摸点大于2
                    this.$transformDom.style.cssText += `;transform: matrix3D(${m.join(',')});`;
                });
            })
            .on('end', (change, done, state) => {
                util.RAF(() => {
                    done();
                    if (state.finger >= 2) {
                        if (this.isLoad && this.canPinch && state.isMove) {
                            // 最大缩放比例校验
                            if (m[0] > this.maxScale) {
                                const PER = this.maxScale / m[0];
                                m = M.trans(PER, state.move, this.origin, M.scale(PER, m));
                            }
                        } else {
                            return;
                        }
                    } else {
                        const K = 200000; // 滑动系数
                        const [finalVX, finalVY] = [state.V.x * K, state.V.y * K];
                        // 避免抖动
                        if (Math.abs(finalVX) < 20 && Math.abs(finalVY) < 20) {
                            state.V.x = 0;
                            state.V.y = 0;
                        }
                        const [changeX, changeY] = getChangeXY({
                            x: change.x + finalVX,
                            y: change.y + finalVY,
                        }, state);
                        // 单指拖拽
                        m = M.translate(changeX, changeY, this.matrix);
                    }

                    // 边界校验
                    const result = util.borderCheck(m, this.origin, this.baseOrigin, { minScale: this.minScale });

                    const ANIMATION_TIME = state.nativeFingers >= state.finger ? 0 : 0.3;

                    // 只有移动才有触发动画的必要【溢出，或者缩小了】
                    state.isMove && this.animation(result.m, ANIMATION_TIME);

                    // 单指触发才有回调的意义
                    if (state.finger == 1 && result.isOver) {
                        this.trigger('end', result, state, this);
                    }
                });
            });
        this.canPinch && this.enablePinch();
    }

    disablePinch() {
        this.canPinch = false;
        this.tap && this.tap.destroy();
    }

    enablePinch() {
        this.canPinch = true;
        this.tap && this.tap.destroy();
        // 双击放大
        this.tap = new Tap(this.$eventDom, 600)
            .on(2, (event) => {
                const t = event.changedTouches ? event.changedTouches[0] : event;
                const center = {
                    x: t.clientX,
                    y: t.clientY,
                    x1: t.clientX,
                    y1: t.clientY,
                };
                this.toggle(center);
            })
            .on(1, () => {
                this.matrix[0] == 1 && this.trigger('close');
            });
    }

    timeout = null

    // 执行动画
    animation = (m, time = 0.3, fn = () => {}) => {
        this.matrix = m;
        // eslint-disable-next-line prefer-destructuring
        this.touch.state.scale = m[0];

        const { $transformDom, $eventDom } = this;
        util.transition([$transformDom], time);
        // eslint-disable-next-line no-unused-expressions
        $transformDom.clientHeight;
        $transformDom.style.cssText += `;transform: matrix3d(${m.join(',')});`;
        clearTimeout(this.timeout);
        const animationEnd = () => {
            clearTimeout(this.timeout);
            util.transition([$transformDom, $eventDom]);
            fn && fn();
        };
        this.animationEnd = animationEnd;
        this.timeout = setTimeout(animationEnd, (time * 1000));
        return this;
    }

    /**
     * 放大缩小切换
     * @param {any} center 缩放中心
     * @returns
     */
    toggle = (center) => {
        if (!this.isLoad) {
            return;
        }
        let { matrix } = this;
        const { origin, baseOrigin } = this;
        const S = matrix[0] > this.minScale ? this.minScale / matrix[0] : this.maxScale / matrix[0];
        matrix = M.trans(S, center, origin, M.scale(S, matrix));
        matrix = util.borderCheck(matrix, origin, baseOrigin, { minScale: this.minScale }).m;
        this.animation(matrix);
    }

    // 销毁
    destroy() {
        [this.tap, this.touch].filter(i => i).forEach(i => i.destroy());
        this.isDestroy = true;
        if (this.$transformDom) {
            this.$transformDom.style.cssText += ';transform: none;';
        }
        return this;
    }

    // 开启
    enable() {
        [this.tap, this.touch].filter(i => i).forEach(i => i.enable());
        return this;
    }

    disable() {
        [this.tap, this.touch].filter(i => i).forEach(i => i.disable());
        return this;
    }
}
